﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using NymphsLib.CommonUtilities.Extensions;
using MovieBrowser.Controller;
using MovieBrowser.Event;
using MovieBrowser.Model;
using MovieBrowser.Presentation.Events;
using MovieBrowser.Presentation.Forms.Db;
using MovieBrowser.Presentation.Forms.Dialogs;
using MovieBrowser.Presentation.List.SearchDialogs;
using MovieBrowser.Util;

namespace MovieBrowser.Presentation.List
{
    public partial class SearchListView : UserControl, IMovieList, IDebugLogEvent
    {
        readonly SaveFileDialog _saveFileDialog = new SaveFileDialog();
        private SearchKeywordsForm _searchKeywordsForm;
        private SearchActorsForm _searchActorsForm;
        private SearchGenresForm _searchGenresForm;
        private SearchDirectorsForm _searchDirectorsForm;
        private SearchLocationForm _searchLocationForm;
        private SearchTextDialog _searchTextForm;
        private SearchRatingDialog _searchRatingForm;

        public MovieBrowserController Controller { get; set; }

        #region Events
        public event EventHandler MovieSelectionChange;
        public event EventHandler CopyMovies;
        public event EventHandler Enqueue;
        public event EventHandler DebugText;
        //
        public void InvokeOnDebugText(Exception exception)
        {
            var handler = DebugText;
            if (handler != null) handler(this, new DebugTextEventArgs(exception));
        }
        public void InvokeOnDebugText(string text)
        {
            var handler = DebugText;
            if (handler != null) handler(this, new DebugTextEventArgs(text));
        }
        //
        public void InvokeOnMovieSelectionChange(EventArgs e)
        {
            var handler = MovieSelectionChange;
            if (handler != null) handler(this, e);
        }
        //
        public void InvokeCopyMovies(CopyMoviesEventArgs e)
        {
            var handler = CopyMovies;
            if (handler != null) handler(this, e);
        }
        //
        public void InvokeEnqueue(CopyMoviesEventArgs e)
        {
            EventHandler handler = Enqueue;
            if (handler != null) handler(this, e);
        }
        #endregion

        public SearchListView()
        {
            InitializeComponent();
            movieListView.Font = Properties.Settings.Default.ListFont;
        }

        private void TbUserPrefWishClick(object sender, EventArgs e)
        {
            tbUserPrefWish.Checked = !tbUserPrefWish.Checked;
            SearchByUserPref();
        }

        private void TbUserPrefLikeClick(object sender, EventArgs e)
        {
            tbUserPrefLike.Checked = !tbUserPrefLike.Checked;
            if (tbUserPrefLike.Checked) tbUserPrefDislike.Checked = false;
            SearchByUserPref();
        }

        private void TbUserPrefDislikeClick(object sender, EventArgs e)
        {
            tbUserPrefDislike.Checked = !tbUserPrefDislike.Checked;
            if (tbUserPrefDislike.Checked) tbUserPrefLike.Checked = false;
            SearchByUserPref();
        }

        private void TbUserPrefWatchedClick(object sender, EventArgs e)
        {
            tbUserPrefWatched.Checked = !tbUserPrefWatched.Checked;
            SearchByUserPref();
        }

        private void TbUserPrefHaveClick(object sender, EventArgs e)
        {

            tbUserPrefHave.Checked = !tbUserPrefHave.Checked;
            SearchByUserPref();
        }

        private void TbUserPrefUserListSelectedIndexChanged(object sender, EventArgs e)
        {
            SearchByUserPref();
        }

        private void ClActorsClick(object sender, EventArgs e)
        {
            GetActorsSearchForm.ShowDialog();
            Search();
        }

        private SearchActorsForm GetActorsSearchForm
        {
            get
            {
                return _searchActorsForm ?? (_searchActorsForm = new SearchActorsForm(Controller));
            }
        }

        private void ClExcelClick(object sender, EventArgs e)
        {
            _saveFileDialog.Filter = @"Excel Document|*.xlsx";

            if (_saveFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                var newFile = new FileInfo(_saveFileDialog.FileName);
                Controller.SaveToExcel(newFile, (from o in movieListView.DataSource as List<Movie> orderby o.Title select o).ToList());
            }
        }

        private void SearchTextSearchStarted(object sender, EventArgs e)
        {
            ComponentUtility.TimedFilter(movieListView, searchText.Text);
        }

        private void SearchTextSearchCancelled(object sender, EventArgs e)
        {
            ComponentUtility.TimedFilter(movieListView, searchText.Text);
        }

        private void DatalistResultSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                var movie = (Movie)(movieListView.SelectedObject);
                if (movie != null)
                {
                    var eventArgs = new MovieEventArgs(movie, "SelectionChanged");
                    InvokeOnMovieSelectionChange(eventArgs);
                }
            }
            catch (Exception exception)
            {
                InvokeOnDebugText(exception);
            }
        }

        private void DatalistResultDoubleClick(object sender, EventArgs e)
        {
            if (movieListView.SelectedObject != null)
                Controller.SearchMovie(MovieBrowserController.ImdbSearch, (Movie)movieListView.SelectedObject);
        }

        private void DatalistResultKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter) return;
            if (movieListView.SelectedObject != null)
                Controller.SearchMovie(MovieBrowserController.ImdbSearch, (Movie)movieListView.SelectedObject);
        }

        private void SearchByUserPref()
        {
            try
            {
                if (Authorization.IsAuthorized)
                {
                    //var movies = Controller.Db.Movies.Select(o => o);
                    var movies = new List<Movie>();

                    if (tbUserPrefLike.Checked)
                    {
                        var list = Controller.Db.MoviePersonalNotes.Where(o => o.User.Id == Authorization.LoggedInUser.Id && o.Favourite > 0).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }
                    if (tbUserPrefDislike.Checked)
                    {
                        var list = Controller.Db.MoviePersonalNotes.Where(o => o.User.Id == Authorization.LoggedInUser.Id && o.Favourite < 0).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }
                    if (tbUserPrefWish.Checked)
                    {
                        var list = Controller.Db.MoviePersonalNotes.Where(o => o.User.Id == Authorization.LoggedInUser.Id && o.Wishlist).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }
                    if (tbUserPrefWatched.Checked)
                    {
                        var list = Controller.Db.MoviePersonalNotes.Where(o => o.User.Id == Authorization.LoggedInUser.Id && o.Seen).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }
                    if (tbUserPrefHave.Checked)
                    {
                        var list = Controller.Db.MoviePersonalNotes.Where(o => o.User.Id == Authorization.LoggedInUser.Id && o.Have).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }

                    if (tbUserPrefUserList.SelectedIndex > 0)
                    {
                        var list = Controller.Db.MovieUserLists.Where(o => o.UserList.User.Id == Authorization.LoggedInUser.Id && o.UserList.ListName == tbUserPrefUserList.Text).Select(o => o.Movie);
                        if (movies.Count == 0) movies.AddRange(list);
                        else movies = movies.Intersect(list).ToList();
                    }
                    movieListView.DataSource = movies;
                }
            }
            catch (Exception exp)
            {
            }
        }

        public Movie SelectedMovie { get { return movieListView.SelectedObject as Movie; } }

        public IList<Movie> SelectedMovies { get { return movieListView.SelectedObjects.Cast<Movie>().ToList(); } }

        public void UpdateNode(Movie movie)
        {
            Controller.UpdateMovieNode(SelectedMovie, movie);
        }

        public void LoadResults(IEnumerable<Movie> movies)
        {
            UpdateAuthorization(movies);
            movieListView.DataSource = movies;
        }

        public void LoadUserList()
        {
            if (Authorization.IsAuthorized)
            {
                tbUserPrefUserList.Items.Clear();
                tbUserPrefUserList.Items.Add("Select Any ...");

                foreach (UserList movieUserList in Controller.Db.UserLists.Where(o => o.User.Id == Authorization.LoggedInUser.Id))
                {
                    tbUserPrefUserList.Items.Add(movieUserList.ListName);
                }
            }
        }

        private void TbRefreshUserListClick(object sender, EventArgs e)
        {
            LoadUserList();
        }

        private void TbUpdateNodesClick(object sender, EventArgs e)
        {
            Controller.UpdateInformation(SelectedMovies, new MovieBrowserController(Controller), true);
        }

        private void TbUpdateKeywordsClick(object sender, EventArgs e)
        {
            Controller.UpdateKeywords(SelectedMovies);
        }

        private void ClKeywordsClick(object sender, EventArgs e)
        {
            GetKeywordsSearchForm.ShowDialog();
            Search();
        }

        private void ClGenresClick(object sender, EventArgs e)
        {
            GetGenresSearchForm.ShowDialog();
            Search();
        }

        private void ClDirectorsClick(object sender, EventArgs e)
        {
            GetDirectorsSearchForm.ShowDialog();
            Search();
        }

        private void Search()
        {
            bool onlyCriteria = true;
            var movies = new List<Movie>();

            if (_searchKeywordsForm != null && _searchKeywordsForm.SearchResult != null && _searchKeywordsForm.SearchResult.Count > 0)
            {
                var list = _searchKeywordsForm.SearchResult;
                var t = Controller.Db.MovieKeywords.WhereIn(o => o.Keyword.Id, list).Select(o => o.Movie);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }

            if (_searchGenresForm != null && _searchGenresForm.SearchResult != null && _searchGenresForm.SearchResult.Count > 0)
            {
                var list = _searchGenresForm.SearchResult;
                var t = Controller.Db.MovieGenres.WhereIn(o => o.Genre.Id, list).Select(o => o.Movie);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }

            if (_searchDirectorsForm != null && _searchDirectorsForm.SearchResult != null && _searchDirectorsForm.SearchResult.Count > 0)
            {
                var list = _searchDirectorsForm.SearchResult;
                var t = Controller.Db.Directors.WhereIn(o => o.Person.Id, list).Select(o => o.Movie);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }

            if (_searchActorsForm != null && _searchActorsForm.SearchResult != null && _searchActorsForm.SearchResult.Count > 0)
            {
                var list = _searchActorsForm.SearchResult;
                var t = Controller.Db.Actors.WhereIn(o => o.Person.Id, list).Select(o => o.Movie);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }
            if (_searchLocationForm != null && _searchLocationForm.SearchResult != null && _searchLocationForm.SearchResult.Count > 0)
            {
                var list = _searchLocationForm.SearchResult;
                var t = Controller.Db.MovieFilesystems.WhereIn(o => o.ComputerName, list).Select(o => o.Movie);
                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();

                if (_searchLocationForm.PathText != "")
                {
                    var t2 = Controller.Db.MovieFilesystems.Where(o => o.FolderPath.Contains(_searchLocationForm.PathText)).Select(o => o.Movie);
                    movies = movies.Intersect(t2).ToList();
                }
                onlyCriteria = false;
            }
            if (_searchRatingForm != null && _searchRatingForm.SearchYear)
            {
                var t = Controller.Db.Movies.Where(o => o.Year >= _searchRatingForm.StartYear && o.Year <= _searchRatingForm.EndYear);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }
            if (_searchRatingForm != null && _searchRatingForm.SearchRating)
            {
                var t = Controller.Db.Movies.Where(o => o.Rating >= _searchRatingForm.StartRating && o.Rating <= _searchRatingForm.EndRating);

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }

            if (_searchTextForm != null && _searchTextForm.SearchHighlight && _searchTextForm.Highlight.Length > 0)
            {
                var t = Controller.Db.Movies.Where(o => o.Highlight.ToLower().Contains(_searchTextForm.Highlight.ToLower()));

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }
            if (_searchTextForm != null && _searchTextForm.SearchStoryline && _searchTextForm.Storyline.Length > 0)
            {
                var t = Controller.Db.Movies.Where(o => o.Storyline.ToLower().Contains(_searchTextForm.Storyline.ToLower()));

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }
            if (_searchTextForm != null && _searchTextForm.SearchTitle && _searchTextForm.Title.Length > 0)
            {
                var t = Controller.Db.Movies.Where(o => o.Title.ToLower().Contains(_searchTextForm.Title.ToLower()));

                if (onlyCriteria) movies.AddRange(t);
                else movies = movies.Intersect(t).ToList();
                onlyCriteria = false;
            }

            UpdateAuthorization(movies);

            movieListView.DataSource = movies;
        }

        private void UpdateAuthorization(IEnumerable<Movie> movies)
        {

            if (Authorization.IsAuthorized)
            {
                foreach (var movie in movies)
                {
                    var pn = Controller.Db.MoviePersonalNotes.Where(o => o.Movie.Id == movie.Id && o.User.Id == Authorization.LoggedInUser.Id).FirstOrDefault();
                    if (pn != null)
                    {
                        movie.Seen = pn.Seen;
                        movie.Have = pn.Have;
                        movie.Watchlist = pn.Wishlist;
                    }
                }
            }
        }

        private void ClLocationClick(object sender, EventArgs e)
        {
            GetLocationSearchForm.ShowDialog();
            Search();
        }


        private void ClYearClick(object sender, EventArgs e)
        {
            GetTextSearchForm.ShowDialog();
            Search();
        }

        private void ClRatingClick(object sender, EventArgs e)
        {
            GetRatingSearchForm.ShowDialog();
            Search();
        }

        //Form Getters
        private SearchKeywordsForm GetKeywordsSearchForm
        {
            get
            {
                return _searchKeywordsForm ?? (_searchKeywordsForm = new SearchKeywordsForm(Controller));
            }
        }
        private SearchGenresForm GetGenresSearchForm
        {
            get
            {
                return _searchGenresForm ?? (_searchGenresForm = new SearchGenresForm(Controller));
            }
        }
        private SearchDirectorsForm GetDirectorsSearchForm
        {
            get
            {
                return _searchDirectorsForm ?? (_searchDirectorsForm = new SearchDirectorsForm(Controller));
            }
        }
        private SearchLocationForm GetLocationSearchForm
        {
            get
            {
                return _searchLocationForm ?? (_searchLocationForm = new SearchLocationForm(Controller));
            }
        }
        private SearchTextDialog GetTextSearchForm
        {
            get
            {
                return _searchTextForm ?? (_searchTextForm = new SearchTextDialog());
            }
        }
        private SearchRatingDialog GetRatingSearchForm
        {
            get
            {
                return _searchRatingForm ?? (_searchRatingForm = new SearchRatingDialog());
            }
        }

        private void TbMovieDbClick(object sender, EventArgs e)
        {
            if (SelectedMovie != null)
                new MovieUpdateForm(Controller.Db, SelectedMovie).Show(this);
        }


        private void DatalistResultFormatRow(object sender, BrightIdeasSoftware.FormatRowEventArgs e)
        {
            ComponentUtility.FormatRow(movieListView, e);
        }

        private void TpCopyMoviesClick(object sender, EventArgs e)
        {
            OnCopyMovies();
        }

        private void SendToPendriveToolStripMenuItemClick(object sender, EventArgs e)
        {
            OnCopyMovies();
        }

        private void OnCopyMovies(bool enqueue = false)
        {
            var movies = new List<Movie>();
            foreach (Movie movie in movieListView.SelectedObjects)
            {
                var movie1 = movie;
                var filesystems = Controller.Db.MovieFilesystems.Where(o => o.Movie.Id == movie1.Id).ToList();
                switch (filesystems.Count)
                {
                    case 1:
                        movie.FilePath = filesystems[0].FolderPath;
                        movies.Add(movie);
                        break;
                    case 0:
                        continue;
                    default:
                        {
                            var selectMovie = new SelectMovie(movie, filesystems);

                            selectMovie.ShowDialog(this);
                            if (String.IsNullOrEmpty(selectMovie.SelectedLocation)) continue;
                            movie.FilePath = selectMovie.SelectedLocation;
                            movies.Add(movie);
                        }
                        break;
                }
            }


            if (movies.Count > 0)
            {

                if (!enqueue)
                {
                    InvokeCopyMovies(new CopyMoviesEventArgs(movies));
                }
                else
                {
                    InvokeEnqueue(new CopyMoviesEventArgs(movies));
                }
            }
        }

        private void AddToQueueToolStripMenuItemClick(object sender, EventArgs e)
        {
            OnCopyMovies(true);
        }

        private void TbEnqueueClick(object sender, EventArgs e)
        {
            OnCopyMovies(true);
        }

        public void ShowGroup(bool group)
        {
            movieListView.ShowGroups = group;
        }

        public void UpdateFont(System.Drawing.Font font)
        {
            movieListView.Font = font;
        }
    }
}
